
package com.hertz.iris.core;

import static com.hertz.iris.util.Constants.Http.HEADER_PARSE_APP_ID;
import static com.hertz.iris.util.Constants.Http.HEADER_PARSE_REST_API_KEY;
import static com.hertz.iris.util.Constants.Http.OPENID_TOKEN;
import static com.hertz.iris.util.Constants.Http.PARSE_APP_ID;
import static com.hertz.iris.util.Constants.Http.PARSE_REST_API_KEY;
import static com.hertz.iris.util.Constants.Http.URL_MATCH_NBA;
import static com.hertz.iris.util.Constants.Http.URL_SAVE_MATCH_ALARM;
import static com.hertz.iris.util.Constants.Http.URL_USER_CREATE_OR_LOGIN;

import java.io.IOException;
import java.net.ConnectException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.github.kevinsawicki.http.HttpRequest;
import com.hertz.iris.MyHttpException;
import com.hertz.iris.pojo.IrisVersion;
import com.hertz.iris.pojo.MatchAlarm;
import com.hertz.iris.pojo.MatchNba;
import com.hertz.iris.pojo.Page;
import com.hertz.iris.pojo.ResponseIsAlert;
import com.hertz.iris.pojo.ResultModel;
import com.hertz.iris.pojo.User;
import com.hertz.iris.util.Constants;
import com.hertz.iris.util.DateUtil;
import com.hertz.iris.util.Ln;
import com.hertz.iris.util.ReturnCodeConstants;

/**
 * Bootstrap API service
 */
public class IrisService {

    private UserAgentProvider userAgentProvider;

    
    /**
     * You can also configure GSON with different naming policies for your API. Maybe your api is Rails
     * api and all json values are lower case with an underscore, like this "first_name" instead of "firstName".
     * You can configure GSON as such below.
     *
     * public static final Gson GSON = new GsonBuilder().setDateFormat("yyyy-MM-dd").setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
     *
     */


    /**
     * Read and connect timeout in milliseconds
     */
    private static final int TIMEOUT = 6 * 1000;
    
    public final static ObjectMapper objectMapper;
	static {
		objectMapper = new ObjectMapper();
		objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 
		objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);  
		objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);  
		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
	}
    
    private static class JsonException extends IOException {

        private static final long serialVersionUID = 3774706606129390273L;

        /**
         * Create exception from {@link JsonParseException}
         *
         * @param cause
         */
        public JsonException(JsonParseException cause) {
            super(cause.getMessage());
            initCause(cause);
        }
    }


    private final String openIdToken;


    /**
     * Create bootstrap service
     *
     * @param userAgentProvider
     * @param openIdToken
     */
    public IrisService(final String openIdToken, final UserAgentProvider userAgentProvider) {
        this.userAgentProvider = userAgentProvider;
        this.openIdToken = openIdToken;
    }

    /**
     * Execute request
     *
     * @param request
     * @return request
     * @throws IOException
     */
    protected HttpRequest execute(HttpRequest request) throws IOException {
        if (!configure(request).ok()){
        	throw new MyHttpException(request.code());
        }
        return request;
    }

    private HttpRequest configure(final HttpRequest request) {
        request.connectTimeout(TIMEOUT).readTimeout(TIMEOUT);
        request.userAgent(userAgentProvider.get());
        request.acceptGzipEncoding();
        request.uncompress(true);
        if(isPostOrPut(request))
            request.contentType(Constants.Http.CONTENT_TYPE_JSON); // All PUT & POST requests to Parse.com api must be in JSON - https://www.parse.com/docs/rest#general-requests

        return addCredentialsTo(request);
    }

    private boolean isPostOrPut(HttpRequest request) {
        return request.getConnection().getRequestMethod().equals(HttpRequest.METHOD_POST)
               || request.getConnection().getRequestMethod().equals(HttpRequest.METHOD_PUT);

    }

    private HttpRequest addCredentialsTo(HttpRequest request) {

        // Required params for
        request.header(HEADER_PARSE_REST_API_KEY, PARSE_REST_API_KEY );
        request.header(HEADER_PARSE_APP_ID, PARSE_APP_ID);
        request.header(OPENID_TOKEN,openIdToken);
        /**
         * NOTE: This may be where you want to add a header for the api token that was saved when you
         * logged in. In the bootstrap sample this is where we are saving the session id as the token.
         * If you actually had received a token you'd take the "apiKey" (aka: token) and add it to the
         * header or form values before you make your requests.
          */

        /**
         * Add the user name and password to the request here if your service needs username or password for each
         * request. You can do this like this:
         * request.basic("myusername", "mypassword");
         */

        return request;
    }

    public List<MatchAlarm> getSyncData(int days) throws IOException{
    	Map<String, Integer> param = new HashMap<String, Integer>();
    	param.put("days", days);
    	HttpRequest request = execute(HttpRequest.post(String.format(Constants.Http.URL_SYNC_ALARM_DATA),param,false));
    	String body = request.body();
    	ResultModel<List<MatchAlarm>> response = objectMapper.readValue(body, new TypeReference<ResultModel<List<MatchAlarm>>>() {});
    	if(response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
        	return response.getData();
        }else{
        	return null;
        }
    }
    
    public ResponseIsAlert isAlert(long mid) throws IOException{
    	Map<String, Object> param = new HashMap<String, Object>();
    	param.put(Constants.Http.MID,mid);
    	HttpRequest request = execute(HttpRequest.post(String.format(Constants.Http.URL_IS_ALERT),param,true));
    	String body = request.body();
        ResultModel<ResponseIsAlert> response = objectMapper.readValue(body, new TypeReference<ResultModel<ResponseIsAlert>>() {});
        if(response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
        	return response.getData();
        }else{
        	return null;
        }
    }
    
    public List<MatchAlarm> getMatchAlarms(String ymd)throws IOException{
    	HttpRequest request = execute(HttpRequest.get(String.format(Constants.Http.URL_MATCH_NBA_ALARM,ymd)));
        ResultModel<Page<MatchAlarm>> response = objectMapper.readValue(request.body(), new TypeReference<ResultModel<Page<MatchAlarm>>>() {});
        if (response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
        	List<MatchAlarm> list = response.getData().getResult();
        	if(list != null && list.size() > 0)
        		DBService.getInstance().saveMatchAlarmLists(list);
            return list;
        }
        return Collections.emptyList();
    }
    
    public boolean postMatchAlarm(MatchAlarm matchAlarm) throws IOException{
//    	execute(HttpRequest.po)
    	Map<String, Object> map = objectMapper.convertValue(matchAlarm, Map.class);
    	HttpRequest request =  execute(HttpRequest.post(URL_SAVE_MATCH_ALARM, map, true));
    	ResultModel<MatchAlarm> response = objectMapper.readValue(request.body(), new TypeReference<ResultModel<MatchAlarm>>() {});
    	if(response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
    		matchAlarm.setId(response.getData().getId());
    		return true;
    	}else 
    		return false;
    }
    
    public boolean cancelMatchAlarm(long mid) throws IOException{
//    	execute(HttpRequest.po)
    	Map<String, Long> param = new HashMap<String, Long>(1);
    	param.put("mid", mid);
    	HttpRequest request =  execute(HttpRequest.post(Constants.Http.URL_CANCEL_ALARM, param, true));
    	ResultModel<Boolean> response = objectMapper.readValue(request.body(), new TypeReference<ResultModel<Boolean>>() {});
    	if(response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
    		return true;
    	}else 
    		return false;
    }
    
    public List<MatchNba> getMatchsNBA(String ymd) throws IOException{
    	HttpRequest request = execute(HttpRequest.get(String.format(URL_MATCH_NBA,ymd)));
        ResultModel<Page<MatchNba>> response = objectMapper.readValue(request.body(), new TypeReference<ResultModel<Page<MatchNba>>>() {});
        if (response != null && response.getCode() == ReturnCodeConstants.SUCCESS)
            return response.getData().getResult();
        return Collections.emptyList();
    }
    /*
     * today
     */
    public List<MatchNba> getMatchsNBA() throws IOException{
    	return getMatchsNBA(DateUtil.today(DateUtil.Y_M_D));
    }
    
    public User getUser(String ssoUserName,String ssoSite)throws IOException,ConnectException{
    	Map<String, String> param = new HashMap<String, String>();
    	param.put(Constants.Http.SSO_USERNAME,ssoUserName);
    	param.put(Constants.Http.SSO_SITE, ssoSite);
    	HttpRequest request = execute(HttpRequest.post(URL_USER_CREATE_OR_LOGIN,param,true));
    	ResultModel<String> response = objectMapper.readValue(request.body(), new TypeReference<ResultModel<String>>() {});
    	if(response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
    		User user = new User();
    		user.setSsoUserName(ssoUserName);
    		user.setSsoSite(ssoSite);
    		user.setOpenIdToken(response.getData());
    		return user;
    	}else{
    		Ln.e("response error",response.getCode());
    		return null;
    	}
    }
    public IrisVersion getVersion() throws IOException{
    	HttpRequest request = execute(HttpRequest.get(Constants.Http.URL_VERSION));
    	ResultModel<IrisVersion> response = objectMapper.readValue(request.body(), new TypeReference<ResultModel<IrisVersion>>() {});
        if (response != null && response.getCode() == ReturnCodeConstants.SUCCESS){
        	return response.getData();
        }else{
        	return null;
        }
    }
}
